﻿// <copyright file="PuzzleChoiceMenu.cs" company="ThorIndustry">Copyright (c) ThorIndustry. All rights reserved.</copyright>

using System.Collections.Generic;

public partial class PuzzleChoiceMenu : Menu
{
    public UnityEngine.Material SolutionMaterial;
    public UnityEngine.GameObject PuzzleButtonPrefab;
    public UnityEngine.GameObject LeftButtonPrefab;
    public UnityEngine.GameObject RightButtonPrefab;

    public UnityEngine.Material HintMaterial;
    public UnityEngine.GameObject SolutionShapePrefab;

    private const int ButtonHeight = 190;
    private const int ButtonWidth = 180;
    private const float OffsetForBackButton = 75;
    private const float MarginLeft = 20;
    private const float MarginRight = 20;
    private const float MarginTop = 10;
    private const float MarginBottom = 5;

    private IViewService viewService;
    private IPuzzleService puzzleService;
    private IDatatableService datatableService;
    private ICameraService cameraService;

    private int numberOfColumn = 0;
    private int numberOfLine = 0;

    private IDatatable<PuzzleDefinition> puzzleDefinitionDatatable;

    private string shapeDefinitionName = string.Empty;
    private List<PuzzleWrapper> availableDefinition = new List<PuzzleWrapper>();

    private Button backButton;
    private Button leftButton;
    private Button rightButton;
    private List<Button> puzzleButtons = new List<Button>();

    public PaletteDefinition CurrentPaletteDefinition
    {
        get
        {
            return this.viewService.CurrentPaletteDefinition;
        }
    }

    public override void Hide()
    {
        base.Hide();

        for (int index = 0; index < this.availableDefinition.Count; ++index)
        {
            UnityEngine.GameObject.Destroy(this.availableDefinition[index].Hint.gameObject);
        }

        this.availableDefinition.Clear();

        for (int index = 0; index < this.puzzleButtons.Count; ++index)
        {
            this.puzzleButtons[index].Clicked -= this.ButtonPuzzleChoice_Clicked;
            UnityEngine.GameObject.Destroy(this.puzzleButtons[index].gameObject);
        }

        this.puzzleButtons.Clear();

        if (this.backButton != null)
        {
            this.backButton.Clicked -= this.ButtonBack_Clicked;
            UnityEngine.GameObject.Destroy(this.backButton.gameObject);
            this.backButton = null;
        }

        if (this.leftButton != null)
        {
            this.leftButton.Clicked -= this.ButtonLeft_Clicked;
            UnityEngine.GameObject.Destroy(this.leftButton.gameObject);
            this.leftButton = null;
        }

        if (this.rightButton != null)
        {
            this.rightButton.Clicked -= this.ButtonRight_Clicked;
            UnityEngine.GameObject.Destroy(this.rightButton.gameObject);
            this.rightButton = null;
        }

        this.isDragging = false;
        this.isDown = false;
        this.mousePositionOnDown = new UnityEngine.Vector3();
    }

    public override void Initialize(IGuiController guiController)
    {
        base.Initialize(guiController);

        this.viewService = Services.GetService<IViewService>();
        this.puzzleService = Services.GetService<IPuzzleService>();
        this.datatableService = Services.GetService<IDatatableService>();
        this.cameraService = Services.GetService<ICameraService>();

        this.puzzleDefinitionDatatable = this.datatableService.GetDatatable<PuzzleDefinition>();
    }

    public override void Show(params object[] parameters)
    {
        this.shapeDefinitionName = string.Empty;
        if (parameters.Length > 0)
        {
            // Find the puzzle name.
            for (int index = 0; index < parameters.Length; ++index)
            {
                if (parameters[index] is string)
                {
                    this.shapeDefinitionName = parameters[index] as string;
                }
            }
        }

        if (string.IsNullOrEmpty(this.shapeDefinitionName))
        {
            this.GuiController.BackMenu();
        }

        // Get the puzzle list.
        this.availableDefinition.Clear();
        foreach (PuzzleDefinition puzzleDefinition in this.puzzleDefinitionDatatable.Values)
        {
            if (puzzleDefinition.ShapesDefinitionName == this.shapeDefinitionName &&
                !puzzleDefinition.Hidden)
            {
                PuzzleWrapper wrapper = new PuzzleWrapper();
                wrapper.PuzzleDefinition = puzzleDefinition;

                PuzzleSolutionShape shape = new PuzzleSolutionShape(puzzleDefinition.Solution);
                shape.Initialize();

                wrapper.Hint = PuzzleView.GenerateSolutionShape(shape, this.CurrentPaletteDefinition.HintColor.Color, this.transform, this.SolutionShapePrefab, 0.3f);

                wrapper.Hint.transform.localScale = new UnityEngine.Vector3(0.3f, 0.3f, 0.3f);

                this.availableDefinition.Add(wrapper);
            }
        }
        
        this.InitializeGui();

        base.Show(parameters);
    }

    protected override void Update()
    {
        base.Update();

        if (this.IsVisible)
        {
            this.UpdateScrollView();
            this.UpdatePuzzleButtonsPosition();
            this.UpdateLeftRightState();
        }
    }

    private void ButtonBack_Clicked(object sender, System.EventArgs args)
    {
        this.GuiController.BackMenu();
    }

    private void ButtonRight_Clicked(object sender, System.EventArgs args)
    {
        // Go to the next column.
        float columnSize = (PuzzleChoiceMenu.MarginLeft + PuzzleChoiceMenu.MarginRight + PuzzleChoiceMenu.ButtonWidth);
        columnSize *= GuiHelper.GetScreenSizeRatio();

        int currentColumn = UnityEngine.Mathf.FloorToInt(this.scrollPosition.x / columnSize);
        if (currentColumn > -this.numberOfColumn)
        {
            this.scrollPosition.x = columnSize * (currentColumn - 1);
        }
        else
        {
            this.scrollPosition.x = columnSize * this.numberOfColumn;
        }

        this.UpdateScrollOffset(new UnityEngine.Vector3());
    }

    private void ButtonLeft_Clicked(object sender, System.EventArgs args)
    {
        // Go to the previous column.
        // Go to the next column.
        float columnSize = (PuzzleChoiceMenu.MarginLeft + PuzzleChoiceMenu.MarginRight + PuzzleChoiceMenu.ButtonWidth);
        columnSize *= GuiHelper.GetScreenSizeRatio();

        int currentColumn = UnityEngine.Mathf.FloorToInt(this.scrollPosition.x / columnSize);
        if (currentColumn < -1)
        {
            this.scrollPosition.x = columnSize * (currentColumn + 1);
        }
        else
        {
            this.scrollPosition.x = 0;
        }

        this.UpdateScrollOffset(new UnityEngine.Vector3());
    }

    private void ButtonPuzzleChoice_Clicked(object sender, System.EventArgs args)
    {
        this.isDown = false;
        this.isDragging = false;

        // Get the sender name and try to open the puzzle!
        if (sender is Button)
        {
            if (this.puzzleService.SelectPuzzle((sender as Button).name) != null)
            {
                this.viewService.Activate(typeof(PuzzleView));
            }
        }
    }

    private void InitializeButton(PuzzleWrapper wrapper, int index)
    {
        UnityEngine.GameObject buttonObject = UnityEngine.GameObject.Instantiate(this.PuzzleButtonPrefab) as UnityEngine.GameObject;
        PuzzleButton currentButton = buttonObject.GetComponent<PuzzleButton>();
        currentButton.name = wrapper.PuzzleDefinition.Name;
        currentButton.IsVisible = this.IsVisible;
        currentButton.Clicked += this.ButtonPuzzleChoice_Clicked;

        // Place the button.
        UnityEngine.Transform buttonTransform = currentButton.transform;
        buttonTransform.parent = this.transform;
        buttonTransform.localPosition = new UnityEngine.Vector3(0, 0, 0);

        currentButton.GuiText.text = wrapper.PuzzleDefinition.LocalizedName;

        // Backgournd rect.
        UnityEngine.Rect backgroundRect = new UnityEngine.Rect(0, 0, PuzzleChoiceMenu.ButtonWidth * GuiHelper.GetScreenSizeRatio(), PuzzleChoiceMenu.ButtonHeight * GuiHelper.GetScreenSizeRatio());
        currentButton.GuiTexture.pixelInset = backgroundRect;

        // Button rect.
        UnityEngine.Rect buttonRect = GuiHelper.FormatGuiTextArea(currentButton.GuiText, 300 * GuiHelper.GetScreenSizeRatio());
        buttonRect.height = 50 * GuiHelper.GetScreenSizeRatio();
        buttonRect.width = backgroundRect.width;
        buttonRect.x = 0;
        buttonRect.y = 0;
        currentButton.GuiTextureButton.pixelInset = buttonRect;

        currentButton.GuiText.pixelOffset = new UnityEngine.Vector2(buttonRect.width / 2, buttonRect.height / 2);

        this.puzzleButtons.Add(currentButton);
    }

    private void InitializeGui()
    {
        this.numberOfLine = UnityEngine.Mathf.FloorToInt((UnityEngine.Screen.height - (OffsetForBackButton * GuiHelper.GetScreenSizeRatio())) / ((PuzzleChoiceMenu.ButtonHeight + PuzzleChoiceMenu.MarginTop + PuzzleChoiceMenu.MarginBottom) * GuiHelper.GetScreenSizeRatio()));
        this.numberOfColumn = UnityEngine.Mathf.CeilToInt(this.availableDefinition.Count / (float)this.numberOfLine);
        this.scrollViewportWidth = (PuzzleChoiceMenu.MarginLeft + PuzzleChoiceMenu.MarginRight + PuzzleChoiceMenu.ButtonWidth) * this.numberOfColumn * GuiHelper.GetScreenSizeRatio();

        // Create one button per shape.
        for (int index = 0; index < this.availableDefinition.Count; ++index)
        {
            this.InitializeButton(this.availableDefinition[index], index);
        }

        this.UpdatePuzzleButtonsPosition();

        // Create a button to go back.
        UnityEngine.GameObject buttonObject = UnityEngine.GameObject.Instantiate(this.HomePrefab) as UnityEngine.GameObject;
        this.backButton = buttonObject.GetComponent<Button>();
        this.backButton.IsVisible = this.IsVisible;
        this.backButton.Clicked += this.ButtonBack_Clicked;

        // Place the button.
        UnityEngine.Transform buttonTransform = this.backButton.transform;
        buttonTransform.parent = this.transform;

        UnityEngine.Rect rect = new UnityEngine.Rect();
        rect.height = 64 * GuiHelper.GetScreenSizeRatio();
        rect.width = 64 * GuiHelper.GetScreenSizeRatio();
        rect.x = -rect.width / 2;
        rect.y = -rect.height / 2;
        this.backButton.GuiTexture.pixelInset = rect;
        buttonTransform.localPosition = new UnityEngine.Vector3(0.5f, 0.1f, 0);

        // Create a button to jump one column on the left.
        buttonObject = UnityEngine.GameObject.Instantiate(this.LeftButtonPrefab) as UnityEngine.GameObject;
        this.leftButton = buttonObject.GetComponent<Button>();
        this.leftButton.IsVisible = this.IsVisible;
        this.leftButton.Clicked += this.ButtonLeft_Clicked;

        // Place the button.
        buttonTransform = this.leftButton.transform;
        buttonTransform.parent = this.transform;

        rect = new UnityEngine.Rect();
        rect.height = 64 * GuiHelper.GetScreenSizeRatio();
        rect.width = 64 * GuiHelper.GetScreenSizeRatio();
        rect.x = -rect.width / 2;
        rect.y = -rect.height / 2;
        this.leftButton.GuiTexture.pixelInset = rect;
        buttonTransform.localPosition = new UnityEngine.Vector3(0.5f - (rect.width / (float)UnityEngine.Screen.width), 0.1f, 0);

        // Create a button to jump one column on the right.
        buttonObject = UnityEngine.GameObject.Instantiate(this.RightButtonPrefab) as UnityEngine.GameObject;
        this.rightButton = buttonObject.GetComponent<Button>();
        this.rightButton.IsVisible = this.IsVisible;
        this.rightButton.Clicked += this.ButtonRight_Clicked;

        // Place the button.
        buttonTransform = this.rightButton.transform;
        buttonTransform.parent = this.transform;

        rect = new UnityEngine.Rect();
        rect.height = 64 * GuiHelper.GetScreenSizeRatio();
        rect.width = 64 * GuiHelper.GetScreenSizeRatio();
        rect.x = -rect.width / 2;
        rect.y = -rect.height / 2;
        this.rightButton.GuiTexture.pixelInset = rect;
        buttonTransform.localPosition = new UnityEngine.Vector3(0.5f + (rect.width / (float)UnityEngine.Screen.width), 0.1f, 0);
        buttonTransform.localEulerAngles = new UnityEngine.Vector3(0, 0, 180);
    }

    private void UpdatePuzzleButtonsPosition()
    {
        float x, y, shapeX, shapeY;
        int column = 0, row = 0;
        UnityEngine.Vector3 screenPosition = new UnityEngine.Vector3();
        UnityEngine.Vector3 worldPosition = new UnityEngine.Vector3();
        for (int index = 0; index < this.puzzleButtons.Count; ++index)
        {            
            // Compute the x, y position in screen space based on the index.
            x = this.scrollPosition.x + (PuzzleChoiceMenu.MarginLeft + ((PuzzleChoiceMenu.MarginLeft + PuzzleChoiceMenu.MarginRight + PuzzleChoiceMenu.ButtonWidth) * column)) * GuiHelper.GetScreenSizeRatio();
            y = (PuzzleChoiceMenu.MarginTop + PuzzleChoiceMenu.MarginBottom + PuzzleChoiceMenu.ButtonHeight) * (row + 1) * GuiHelper.GetScreenSizeRatio();
            
            // Then add some displacement due to the mouse or touch.
            screenPosition.x = x / UnityEngine.Screen.width;
            screenPosition.y = (UnityEngine.Screen.height - y) / UnityEngine.Screen.height;

            // Apply the position to the button.
            this.puzzleButtons[index].transform.localPosition = screenPosition;

            // Apply the position to the shape.    
            shapeX = x + (PuzzleChoiceMenu.ButtonWidth / 2) * GuiHelper.GetScreenSizeRatio();
            shapeY = y - ((PuzzleChoiceMenu.ButtonHeight - 25) / 2) * GuiHelper.GetScreenSizeRatio();
            worldPosition = this.cameraService.Camera.ScreenToWorldPoint(new UnityEngine.Vector3(shapeX, (UnityEngine.Screen.height - shapeY), 0));
            worldPosition.z = 1;

            // Center the shape.
            UnityEngine.Vector3 center = this.availableDefinition[index].Hint.GetLocalCenter();
            
            worldPosition.x = worldPosition.x - center.x;
            // worldPosition.y = worldPosition.y + this.availableDefinition[index].Hint.HalfHeightScaled;

            this.availableDefinition[index].Hint.transform.position = worldPosition;

            column++;
            if (column >= this.numberOfColumn)
            {
                column = 0;
                row++;
            }
        }
    }

    private void UpdateLeftRightState()
    {
        // Should left be enabled?
        float columnSize = (PuzzleChoiceMenu.MarginLeft + PuzzleChoiceMenu.MarginRight + PuzzleChoiceMenu.ButtonWidth) * GuiHelper.GetScreenSizeRatio();

        int currentColumn = UnityEngine.Mathf.FloorToInt(this.scrollPosition.x / columnSize);
        this.leftButton.Enable(currentColumn <= -1);
        this.rightButton.Enable((this.scrollPosition.x + this.scrollViewportWidth) > UnityEngine.Screen.width);
    }

    public class PuzzleWrapper
    {
        public PuzzleDefinition PuzzleDefinition
        {
            get;
            set;
        }

        public SolutionShape Hint
        {
            get;
            set;
        }
    }
}
